package com.jlf.starter.logger.reactive.decorator;

import com.jlf.starter.logger.reactive.ApiLogger;
import com.jlf.starter.logger.reactive.entity.LogObject;
import com.jlf.starter.logger.reactive.provider.BodyProvider;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.lang.NonNull;
import org.springframework.util.FastByteArrayOutputStream;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;

/**
 * 请求参数的装饰器
 *
 * @author wujr
 * 2023/8/3
 * @version 1.0
 * 【变更历史】
 * [wujr 2023/8/3 1.0 新增]
 */
@Slf4j
public class RequestDecorator extends ServerHttpRequestDecorator {
    private final BodyProvider bodyProvider = new BodyProvider();
    private final LogObject logObject;
    public RequestDecorator(ServerHttpRequest delegate, LogObject logObject) {
        super(delegate);
        this.logObject = logObject;
    }

    @Override
    @NonNull
    public Flux<DataBuffer> getBody() {
        return super.getBody()
                .switchIfEmpty(Flux.<DataBuffer>empty()
                        .doOnComplete(() -> {ApiLogger.log(logObject);}))
                .doOnNext(dataBuffer -> {
                    String fullBodyMessage = bodyProvider.createBodyMessage(copyBodyBuffer(dataBuffer));
                    logObject.setRequestBody(fullBodyMessage);
                });
    }


    private FastByteArrayOutputStream copyBodyBuffer(DataBuffer buffer) {
        try {
            FastByteArrayOutputStream bodyStream = new FastByteArrayOutputStream();
            ByteBuffer tmp = ByteBuffer.allocate(buffer.capacity());
            buffer.toByteBuffer(tmp);
            Channels.newChannel(bodyStream).write(tmp.asReadOnlyBuffer());
            return bodyStream;
        } catch (IOException e) {
            log.error("复制body内容异常：", e);
            throw new RuntimeException(e);
        }
    }

    public static ServerHttpRequest build(ServerWebExchange exchange, LogObject logObject){
        if (logObject.hasBody()){
            // 有内容，需要装饰
            MediaType mediaType = exchange.getRequest().getHeaders().getContentType();
            if (mediaType != null && mediaType.includes(MediaType.MULTIPART_FORM_DATA)){
                // 这是上传文件，不记录具体内容
                return exchange.getRequest();
            }
            return new RequestDecorator(exchange.getRequest(), logObject);
        }else{
            return exchange.getRequest();
        }
    }
}
